/**
 * @file
 *
 * @date 13.01.2017
 * @author Anton Bondarev
 */

#ifndef MII_H_
#define MII_H_

#define MII_BMCR        0x00 /* Basic mode control register */
#define MII_BMSR        0x01 /* Basic mode status register */
#define MII_PHYSID1     0x02 /* PHYS ID 1 */
#define MII_PHYSID2     0x03 /* PHYS ID 2 */
#define MII_ADVERTISE   0x04 /* Advertisement control reg */
#define MII_LPA         0x05 /* Link partner ability reg */
#define MII_EXPANSION   0x06 /* Expansion register */
#define MII_CTRL1000    0x09 /* 1000BASE-T control */
#define MII_STAT1000    0x0a /* 1000BASE-T status */
#define MII_ESTATUS     0x0f /* Extended Status */
#define MII_DCOUNTER    0x12 /* Disconnect counter */
#define MII_FCSCOUNTER  0x13 /* False carrier counter */
#define MII_NWAYTEST    0x14 /* N-way auto-neg test reg */
#define MII_RERRCOUNTER 0x15 /* Receive error counter */
#define MII_SREVISION   0x16 /* Silicon revision */
#define MII_RESV1       0x17 /* Reserved... */
#define MII_LBRERROR    0x18 /* Lpback, rx, bypass error */
#define MII_PHYADDR     0x19 /* PHY address */
#define MII_RESV2       0x1a /* Reserved... */
#define MII_TPISTATUS   0x1b /* TPI status for 10mbps */
#define MII_NCONFIG     0x1c /* Network interface config */

/* Link partner ability register. */
#define LPA_SLCT            0x001f /* Same as advertise selector */
#define LPA_10HALF          0x0020 /* Can do 10mbps half-duplex */
#define LPA_1000XFULL       0x0020 /* Can do 1000BASE-X full-duplex */
#define LPA_10FULL          0x0040 /* Can do 10mbps full-duplex */
#define LPA_1000XHALF       0x0040 /* Can do 1000BASE-X half-duplex */
#define LPA_100HALF         0x0080 /* Can do 100mbps half-duplex */
#define LPA_1000XPAUSE      0x0080 /* Can do 1000BASE-X pause */
#define LPA_100FULL         0x0100 /* Can do 100mbps full-duplex */
#define LPA_1000XPAUSE_ASYM 0x0100 /* Can do 1000BASE-X pause asym*/
#define LPA_100BASE4        0x0200 /* Can do 100mbps 4k packets */
#define LPA_PAUSE_CAP       0x0400 /* Can pause */
#define LPA_PAUSE_ASYM      0x0800 /* Can pause asymetrically */
#define LPA_RESV            0x1000 /* Unused... */
#define LPA_RFAULT          0x2000 /* Link partner faulted */
#define LPA_LPACK           0x4000 /* Link partner acked us */
#define LPA_NPAGE           0x8000 /* Next page bit */

#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL)
#define LPA_10     (LPA_10FULL | LPA_10HALF)
#define LPA_100    (LPA_100FULL | LPA_100HALF)
#define LPA_1000   (LPA_1000XFULL | LPA_1000XHALF)

/* 1000BASE-T Status register */
#define LPA_1000LOCALRXOK 0x2000 /* Link partner local receiver status */
#define LPA_1000REMRXOK   0x1000 /* Link partner remote receiver status */
#define LPA_1000FULL      0x0800 /* Link partner 1000BASE-T full duplex */
#define LPA_1000HALF      0x0400 /* Link partner 1000BASE-T half duplex */

/* Basic mode control register. */
#define BMCR_RESV      0x003f /* Unused... */
#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
#define BMCR_CTST      0x0080 /* Collision test */
#define BMCR_FULLDPLX  0x0100 /* Full duplex */
#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
#define BMCR_ISOLATE   0x0400 /* Disconnect DP83840 from MII */
#define BMCR_PDOWN     0x0800 /* Powerdown the DP83840 */
#define BMCR_ANENABLE  0x1000 /* Enable auto negotiation */
#define BMCR_SPEED100  0x2000 /* Select 100Mbps */
#define BMCR_LOOPBACK  0x4000 /* TXD loopback bits */
#define BMCR_RESET     0x8000 /* Reset the DP83840 */

/* Basic mode status register. */
#define BMSR_ERCAP        0x0001 /* Ext-reg capability */
#define BMSR_JCD          0x0002 /* Jabber detected */
#define BMSR_LSTATUS      0x0004 /* Link status */
#define BMSR_ANEGCAPABLE  0x0008 /* Able to do auto-negotiation */
#define BMSR_RFAULT       0x0010 /* Remote fault detected */
#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
#define BMSR_RESV         0x00c0 /* Unused... */
#define BMSR_ESTATEN      0x0100 /* Extended Status in R15 */
#define BMSR_100HALF2     0x0200 /* Can do 100BASE-T2 HDX */
#define BMSR_100FULL2     0x0400 /* Can do 100BASE-T2 FDX */
#define BMSR_10HALF       0x0800 /* Can do 10mbps, half-duplex */
#define BMSR_10FULL       0x1000 /* Can do 10mbps, full-duplex */
#define BMSR_100HALF      0x2000 /* Can do 100mbps, half-duplex */
#define BMSR_100FULL      0x4000 /* Can do 100mbps, full-duplex */
#define BMSR_100BASE4     0x8000 /* Can do 100mbps, 4k packets */

/* Advertisement control register. */
#define ADVERTISE_SLCT          0x001f /* Selector bits */
#define ADVERTISE_CSMA          0x0001 /* Only selector supported */
#define ADVERTISE_10HALF        0x0020 /* Try for 10mbps half-duplex */
#define ADVERTISE_1000XFULL     0x0020 /* Try for 1000BASE-X full-duplex */
#define ADVERTISE_10FULL        0x0040 /* Try for 10mbps full-duplex */
#define ADVERTISE_1000XHALF     0x0040 /* Try for 1000BASE-X half-duplex */
#define ADVERTISE_100HALF       0x0080 /* Try for 100mbps half-duplex */
#define ADVERTISE_1000XPAUSE    0x0080 /* Try for 1000BASE-X pause */
#define ADVERTISE_100FULL       0x0100 /* Try for 100mbps full-duplex */
#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */
#define ADVERTISE_100BASE4      0x0200 /* Try for 100mbps 4k packets */
#define ADVERTISE_PAUSE_CAP     0x0400 /* Try for pause */
#define ADVERTISE_PAUSE_ASYM    0x0800 /* Try for asymetric pause */
#define ADVERTISE_RESV          0x1000 /* Unused... */
#define ADVERTISE_RFAULT        0x2000 /* Say we can detect faults */
#define ADVERTISE_LPACK         0x4000 /* Ack link partners response */
#define ADVERTISE_NPAGE         0x8000 /* Next page bit */

#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | ADVERTISE_CSMA)

#define ADVERTISE_ALL                                        \
	(ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF \
	    | ADVERTISE_100FULL)

/* 1000BASE-T Control register */
#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */
#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */

/* Some values interleave for 10/1000 modes, so wee need another numeration */
#define NET_10HALF   (1 << 0)
#define NET_10FULL   (1 << 1)
#define NET_100HALF  (1 << 2)
#define NET_100FULL  (1 << 3)
#define NET_1000HALF (1 << 4)
#define NET_1000FULL (1 << 5)

#define NET_GBIT (NET_1000HALF | NET_1000FULL)

/**
 * mii_nway_result
 * @negotiated: value of MII ANAR and'd with ANLPAR
 *
 * Given a set of MII abilities, check each bit and returns the
 * currently supported media, in the priority order defined by
 * IEEE 802.3u. We use LPA_xxx constants but note this is not the
 * value of LPA solely, as described above.
 *
 * The one exception to IEEE 802.3u is that 100baseT4 is placed
 * between 100T-full and 100T-half. If your phy does not support
 * 100T4 this is fine. If your phy places 100T4 elsewhere in the
 * priority order, you will need to roll your own function.
 */
static inline unsigned int mii_nway_result(unsigned int negotiated) {
	unsigned int ret;

	if (negotiated & LPA_100FULL)
		ret = LPA_100FULL;
	else if (negotiated & LPA_100BASE4)
		ret = LPA_100BASE4;
	else if (negotiated & LPA_100HALF)
		ret = LPA_100HALF;
	else if (negotiated & LPA_10FULL)
		ret = LPA_10FULL;
	else
		ret = LPA_10HALF;

	return ret;
}
static inline int net_speed_to_adv(int speed) {
	int res = 0;
	if (speed & NET_10HALF) {
		res |= ADVERTISE_10HALF;
	}
	if (speed & NET_10FULL) {
		res |= ADVERTISE_10FULL;
	}
	if (speed & NET_100HALF) {
		res |= ADVERTISE_100HALF;
	}
	if (speed & NET_100FULL) {
		res |= ADVERTISE_100FULL;
	}
	if (speed & NET_1000HALF) {
		res |= ADVERTISE_1000XHALF;
	}
	if (speed & NET_1000FULL) {
		res |= ADVERTISE_1000XFULL;
	}

	return res;
}

static inline int adv_to_net_speed(int adv, int is_gigabit) {
	int res = 0;
	if (is_gigabit) {
		if (adv & ADVERTISE_1000XHALF) {
			res |= NET_1000HALF;
		}
		if (adv & ADVERTISE_1000XFULL) {
			res |= NET_1000FULL;
		}
	}
	else {
		if (adv & ADVERTISE_10HALF) {
			res |= NET_10HALF;
		}
		if (adv & ADVERTISE_10FULL) {
			res |= NET_10FULL;
		}
		if (adv & ADVERTISE_100HALF) {
			res |= NET_100HALF;
		}
		if (adv & ADVERTISE_100FULL) {
			res |= NET_100FULL;
		}
	}

	return res;
}

static inline int net_is_10(int net_speed) {
	return !!(net_speed & (NET_10HALF | NET_10FULL));
}

static inline int net_is_100(int net_speed) {
	return !!(net_speed & (NET_100HALF | NET_100FULL));
}

static inline int net_is_1000(int net_speed) {
	return !!(net_speed & (NET_1000HALF | NET_1000FULL));
}

static inline int net_to_mbps(int net_speed) {
	if (net_is_10(net_speed)) {
		return 10;
	}

	if (net_is_100(net_speed)) {
		return 100;
	}

	if (net_is_1000(net_speed)) {
		return 1000;
	}

	return 0;
}

static inline int net_is_fullduplex(int net_speed) {
	return !!(net_speed & (NET_10FULL | NET_100FULL | NET_1000FULL));
}

static inline int net_top_speed(int net_speed) {
	if (net_speed & NET_1000FULL) {
		return NET_1000FULL;
	}
	else if (net_speed & NET_1000HALF) {
		return NET_1000HALF;
	}
	else if (net_speed & NET_100FULL) {
		return NET_100FULL;
	}
	else if (net_speed & NET_100HALF) {
		return NET_100HALF;
	}
	else if (net_speed & NET_10FULL) {
		return NET_10FULL;
	}
	else if (net_speed & NET_10HALF) {
		return NET_10HALF;
	}

	return 0;
}

#endif /* MII_H_ */
